﻿/*
 * shaim
 * http://shaim.net
 * Copyright ©2005-2008, shaim dev team
 * Licensed under the Massachusetts Institute of Technology (MIT) License
 * http://www.opensource.org/licenses/mit-license.php
 * 
 */

using System;
using System.ComponentModel;
using System.Globalization;
using System.Xml;
using csammisrun.shaim.ContactListInternal;

namespace csammisrun.shaim
{
    /// <summary>
    /// Groups together one or more <see cref="MetaContact"/> and <see cref="ChatRoom"/> objects
    /// </summary>
    public class ContactGroup : IComparable, IContactListSerializable, IOrderableElement, INotifyPropertyChanged
    {
        private readonly BindingListCollection<MetaContact> contacts;
        private readonly ContactList parentList;
        private string displayName;
        private object rawGroup;
        private int onlineCount;
        private bool collapsed; // Contact groups are expanded (collapsed = false) by default
        private int preferredOrder = -1;

        /// <summary>
        /// Handles an event that has updated the <see cref="ContactGroup.Contacts"/> list
        /// </summary>
        public delegate void ContactListModifiedHandler(MetaContact smc);

        /// <summary>
        /// Handles changes in the properties of the group's Contacts list
        /// </summary>
        protected virtual void contacts_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "OnlineCount")
            {
                OnlineCount = contacts.OnlineCount;
            }
            else if (e.PropertyName == "Items")
            {
                // Channel the sublist's count update to the group's own count property
                OnPropertyChanged("Count");
            }
        }

        /// <summary>
        /// Initializes a new ContactGroup with the specified display name
        /// </summary>
        internal ContactGroup(ContactList parentList, string displayname, ICore core)
        {
            this.parentList = parentList;
            DisplayName = displayname;
            IUIDispatcher uidispatcher = (core != null && core.UISet != null) ? core.UISet.CreateDispatcher() : null;
            contacts = new BindingListCollection<MetaContact>(uidispatcher);
            contacts.PropertyChanged += contacts_PropertyChanged;
        }

        #region Properties
        /// <summary>
        /// Gets the parent ContactList that owns the group
        /// </summary>
        internal ContactList ParentList
        {
            get { return parentList; }
        }

        /// <summary>
        /// The underlying list of <see cref="MetaContact"/> objects that are
        /// abstracted by this group
        /// </summary>
        public BindingListCollection<MetaContact> Contacts
        {
            get { return contacts; }
        }

        /// <summary>
        /// The name displayed to the shaim user
        /// </summary>
        public string DisplayName
        {
            get { return displayName; }
            set
            {
                if (displayName != value)
                {
                    displayName = value;
                    OnPropertyChanged("DisplayName");
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether this group is virtual (i.e., not created by a protocol)
        /// </summary>
        public virtual bool IsVirtual
        {
            get { return false; }
        }

        /// <summary>
        /// Gets or sets a value controlling the display of this group
        /// </summary>
        public bool Collapsed
        {
            get { return collapsed; }
            set
            {
                if (collapsed != value)
                {
                    collapsed = value;
                    OnPropertyChanged("Collapsed");
                }
            }
        }

        /// <summary>
        /// The raw contact object received by the protocol plugin that generated this group
        /// </summary>
        public object RawGroup
        {
            get { return rawGroup; }
            set { rawGroup = value; }
        }

        /// <summary>
        /// Gets the number of metacontacts that are online within this group
        /// </summary>
        public virtual int OnlineCount
        {
            get { return onlineCount; }
            internal set
            {
                if (value != onlineCount)
                {
                    onlineCount = value;
                    OnPropertyChanged("OnlineCount");
                }
            }
        }

        /// <summary>
        /// Gets the number of metacontacts that are contained by this group
        /// </summary>
        public virtual int Count
        {
            get { return contacts.TotalCount; }
        }
        #endregion

        #region IComparable Members
        /// <summary>
        /// Two ContactGroup objects are equal if Names are equal and they are both either virtual or non-virtual
        /// </summary>
        /// <param name="obj">The object to which to compare this instance</param>
        /// <returns>0 if the objects are equal, +/- 1 otherwise</returns>
        public int CompareTo(object obj)
        {
            ContactGroup scg = obj as ContactGroup;
            if (scg != null)
            {
                if (IsVirtual && !scg.IsVirtual)
                {
                    return 1;
                }
                else if (!IsVirtual && scg.IsVirtual)
                {
                    return -1;
                }
                else
                {
                    return DisplayName.CompareTo(scg.DisplayName);
                }
            }
            return -1;
        }

        /// <summary>
        /// Determines whether two ContactGroup instances are equal.
        /// </summary>
        /// <param name="obj">The Object to compare with the current ContactGroup.</param>
        /// <returns>true if the specified Object is equal to the current ContactGroup; otherwise, false.</returns>
        public override bool Equals(object obj)
        {
            return (CompareTo(obj) == 0);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>A hash code for the current ContactGroup.</returns>
        public override int GetHashCode()
        {
            return DisplayName.GetHashCode();
        }

        #endregion

        #region IContactListSerializable Members
        /// <summary>
        /// Gets the XPath expression that will locate the given node relative to its parent
        /// </summary>
        public string XPath
        {
            get { return String.Format(CultureInfo.InvariantCulture, "Group[@Name='{0}']", ContactList.EscapeAttribute(DisplayName)); }
        }
        /// <summary>
        /// Sets item-specific attributes on an XmlNode
        /// </summary>
        public virtual void SetXmlNodeAttributes(XmlNode node)
        {
            XmlAttribute groupname = node.OwnerDocument.CreateAttribute("Name");
            groupname.Value = ContactList.EscapeAttribute(DisplayName);
            node.Attributes.SetNamedItem(groupname);

            XmlAttribute groupcollapsed = node.OwnerDocument.CreateAttribute("Collapsed");
            groupcollapsed.Value = (Collapsed) ? "Yes" : "No";
            node.Attributes.SetNamedItem(groupcollapsed);
        }
        #endregion

        #region INotifyPropertyChanged Members
        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event
        /// </summary>
        /// <param name="propertyName">The name of the property that changed</param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        /// <summary>
        /// Returns a string representation of a ContactGroup
        /// </summary>
        public override string ToString()
        {
            return String.Format(CultureInfo.InvariantCulture, "Group: {0}", DisplayName);
        }

        #region IOrderableElement Members

        /// <summary>
        /// Gets the preferred order of the list element
        /// </summary>
        public int PreferredOrder
        {
            get { return preferredOrder; }
            internal set { preferredOrder = value; }
        }

        #endregion
    }
}
